package net.apexes.commons.lang;

import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author hedyn
 */
public class NetworkTimeMillisException extends Exception {

    private final List<Throwable> causes = new ArrayList<>();

    NetworkTimeMillisException(String message) {
        super(message);
    }

    NetworkTimeMillisException(Map<InetSocketAddress, Long> timeMap, Map<InetSocketAddress, Throwable> causeMap) {
        super(message(timeMap, causeMap));

        if (!causeMap.isEmpty()) {
            Throwable cause = null;
            Throwable last = null;
            for (Throwable throwable : causeMap.values()) {
                causes.add(throwable);
                if (cause == null) {
                    cause = throwable;
                } else if (last != null) {
                    last.initCause(throwable);
                }
                last = findLastCause(throwable);
            }
            initCause(cause);
        }
    }

    public boolean isCauseWith(Class<? extends Throwable> throwableClass) {
        for (Throwable cause : causes) {
            if (throwableClass.isAssignableFrom(cause.getClass())) {
                return true;
            }
        }
        return false;
    }

    private static String message(Map<InetSocketAddress, Long> timeMap, Map<InetSocketAddress, Throwable> causeMap) {
        StringBuilder sb = new StringBuilder();
        boolean first = true;
        for (Map.Entry<InetSocketAddress, Long> entry : timeMap.entrySet()) {
            if (first) {
                first = false;
            } else {
                sb.append(", ");
            }
            sb.append(entry.getKey());
            sb.append(" = ");
            sb.append(entry.getValue());
        }
        for (Map.Entry<InetSocketAddress, Throwable> entry : causeMap.entrySet()) {
            if (first) {
                first = false;
            } else {
                sb.append(", ");
            }
            sb.append(entry.getKey());
            sb.append(" = ");
            sb.append(entry.getValue().getClass().getSimpleName());
        }
        return sb.toString();
    }

    private static Throwable findLastCause(Throwable throwable) {
        Throwable last = throwable;
        while (true) {
            Throwable cause = last.getCause();
            if (cause == null) {
                break;
            } else {
                last = cause;
            }
        }
        return last;
    }

}
